Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

85장. 컨테이너 로깅 — FireLens · awslogs 드라이버

이 장에서 말하고자 하는 것

컨테이너의 표준 로그 방식은

표준 출력(stdout) / 표준 에러(stderr) 에 쓴다

다.

이 출력을 어떻게 CloudWatch (또는 다른 백엔드) 로 보낼지가
컨테이너 로깅 드라이버 의 일이다.

ECS에서는 두 가지가 흔하다.

  • awslogs 드라이버
  • FireLens (Fluent Bit / Fluentd 사이드카)

1. awslogs 드라이버 — 가장 간단

ECS Task Definition에 다음을 추가하면 끝.

"logConfiguration": {
  "logDriver": "awslogs",
  "options": {
    "awslogs-group":         "/ecs/orders",
    "awslogs-region":        "ap-northeast-2",
    "awslogs-stream-prefix": "app"
  }
}

ECS 에이전트가 컨테이너의 stdout/stderr를 CloudWatch Logs로 직접 보낸다.

  • 설정이 단순
  • 컨테이너 이미지에 추가 사이드카 필요 없음
  • 보낼 곳을 CloudWatch 외에 못 정함

단순한 서비스는 거의 항상 awslogs로 충분


2. FireLens — 더 유연한 로깅 사이드카

ECS Task에 Fluent Bit (또는 Fluentd) 사이드카 를 띄워
로그를 다양한 백엔드로 보낸다.

[app container] → stdout
                    ↓
              [Fluent Bit 사이드카]
                    ↓ 라우팅
              ├─ CloudWatch Logs
              ├─ Kinesis Firehose → S3
              ├─ Datadog
              ├─ OpenSearch
              └─ Splunk
  • 다중 백엔드
  • 필터링 · 파싱 · 변환 가능
  • 운영 로그와 분석용 로그를 다른 곳으로 분리 가능

3. 언제 FireLens가 필요한가

1. 로그를 여러 백엔드로

  • 운영용: CloudWatch
  • 장기 보관: S3
  • 분석: OpenSearch / Datadog

2. 사전 처리가 필요

  • 민감 정보 마스킹
  • 필드 추가/삭제
  • 다른 포맷으로 변환

3. 비용 절감

  • 디버그 로그는 S3 (Glacier) 로 직행
  • 운영 로그만 CloudWatch (비싸지만 검색 빠름)

4. 멀티 클라우드 표준

  • Fluent Bit은 AWS · GCP · Azure 어디서나 동일

4. 사이드카 패턴

Task 안에 두 개의 컨테이너 가 함께 돈다.

Task
 ├─ app          (essential = true)
 └─ log_router   (essential = false, Fluent Bit)
  • app 죽으면 Task 죽음
  • log_router 죽어도 app은 살아 있음 (essential = false)

5. 로그 형식 — JSON 한 줄

어떤 드라이버를 쓰든 가장 중요한 건 JSON 한 줄로 출력 하는 것이다.

{"ts":"2026-01-01T10:00:00Z","level":"info","trace_id":"abc","msg":"order created","order_id":"o-1","user_id":"u-1"}
  • CloudWatch Logs Insights가 자동 인식
  • Fluent Bit · OpenSearch 모두 자연스럽게 처리

Logger 라이브러리에서 JSON 출력을 기본으로 설정


6. EMF — 로그 한 줄로 메트릭까지

CloudWatch Embedded Metric Format 을 쓰면
로그 출력만으로 메트릭이 자동 생성된다.

{
  "_aws": {
    "CloudWatchMetrics": [{
      "Namespace": "MyApp/Orders",
      "Dimensions": [["Service"]],
      "Metrics": [{ "Name": "OrdersCreated", "Unit": "Count" }]
    }]
  },
  "Service": "orders",
  "OrdersCreated": 1
}
  • PutMetricData 호출 안 함 (비용 절감)
  • 로그가 곧 메트릭의 원본
  • 라이브러리 (aws-embedded-metrics) 사용이 일반적

7. 우리 서비스에서

단순 서비스 (대다수)

ECS Task → awslogs → /ecs/<service-name>

핵심 서비스 (payments · audit)

ECS Task → FireLens (Fluent Bit 사이드카)
  ├─ CloudWatch Logs (운영 검색)
  └─ Kinesis Firehose → S3 (Glacier, 장기 감사)

용도가 단순하면 awslogs, 복잡한 라우팅이 필요하면 FireLens.


8. 직접 확인해보기 — Task Definition (FireLens 예)

{
  "family": "orders",
  "containerDefinitions": [
    {
      "name": "app",
      "image": "<ecr>/orders:v1",
      "essential": true,
      "logConfiguration": {
        "logDriver": "awsfirelens"
      }
    },
    {
      "name": "log_router",
      "image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:stable",
      "essential": false,
      "firelensConfiguration": {
        "type": "fluentbit",
        "options": { "enable-ecs-log-metadata": "true" }
      },
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group":  "/ecs/firelens",
          "awslogs-region": "ap-northeast-2",
          "awslogs-stream-prefix": "router"
        }
      }
    }
  ]
}

app은 awsfirelens 드라이버로 보내고, 라우터의 출력만 awslogs.


9. 코드로는 이렇게 생겼다 — Terraform (FireLens Task)

resource "aws_ecs_task_definition" "orders" {
  family                   = "orders"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = "512"
  memory                   = "1024"
  execution_role_arn       = aws_iam_role.task_execution.arn
  task_role_arn            = aws_iam_role.orders_task.arn

  container_definitions = jsonencode([
    {
      name      = "app"
      image     = "${aws_ecr_repository.orders.repository_url}:v1"
      essential = true
      portMappings = [{ containerPort = 8080 }]
      logConfiguration = { logDriver = "awsfirelens" }
    },
    {
      name      = "log_router"
      image     = "public.ecr.aws/aws-observability/aws-for-fluent-bit:stable"
      essential = false
      firelensConfiguration = {
        type    = "fluentbit"
        options = { enable-ecs-log-metadata = "true" }
      }
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          awslogs-group         = "/ecs/firelens"
          awslogs-region        = "ap-northeast-2"
          awslogs-stream-prefix = "router"
        }
      }
    }
  ])
}

Fluent Bit의 라우팅 규칙은 별도 ConfigMap (또는 S3) 파일로 둔다.


10. 이렇게 쓰면 망한다 — 안티패턴

안티패턴 1. 컨테이너 안에 파일로 로그를 쓴다

컨테이너 죽으면 사라진다.

항상 stdout / stderr

안티패턴 2. 한 줄 로그가 여러 줄에 걸친다 (stack trace 등)

파서가 깨진다.

멀티라인은 한 JSON 문자열로 묶거나 라우터에서 자동 합치기 설정

안티패턴 3. FireLens를 모든 서비스에 적용

단순한 서비스에 사이드카는 오버헤드.

단순함이 답인 곳은 그대로 awslogs

안티패턴 4. 로깅 사이드카에 충분한 자원을 안 준다

Fluent Bit이 메모리 부족으로 죽으면 로그 손실.


11. 한 줄로 정리

컨테이너 로그는 stdout으로, awslogs (단순) 또는 FireLens (다중/변환) 로 보낸다
JSON 한 줄과 trace_id 가 핵심이다


12. 이 장의 핵심 정리

  1. 컨테이너 로그는 stdout/stderr 가 표준.
  2. ECS는 awslogs 드라이버로 CloudWatch에 바로 보낼 수 있다.
  3. FireLens는 다중 백엔드 · 변환 · 비용 최적화에 강하다.
  4. 로그는 JSON 한 줄로, trace_id · user_id 같은 필드를 포함.
  5. EMF로 로그가 곧 메트릭이 되는 패턴이 가능하다.
  6. 단순 서비스는 awslogs, 복잡한 라우팅은 FireLens.